import json

from django.shortcuts import render

# Create your views here.
from django.views import View

from django.http import JsonResponse, HttpResponse

"""
判断用户名是否重复的功能。
前端(了解)：     当用户输入用户名之后，失去焦点， 发送一个axios(ajax)请求
后端（思路）：
    请求:         接收用户名 
    业务逻辑：     
                    根据用户名查询数据库，如果查询结果数量等于0，说明没有注册
                    如果查询结果数量等于1，说明有注册
    响应          JSON 
                {code:0,count:0/1,errmsg:ok}

    路由      GET         usernames/<username>/count/        
   步骤：
        1.  接收用户名
        2.  根据用户名查询数据库
        3.  返回响应         

"""

from apps.users.models import User


class UsernameCountView(View):
    def get(self, request, username):
        count = User.objects.filter(username=username).count()
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'count': count})


##############################################################################################
"""
判断手机号是否重复的功能。
前端(了解)：     当用户输入用户名之后，失去焦点， 发送一个axios(ajax)请求
后端（思路）：
    请求:         接收用户名 
    业务逻辑：     
                    根据用户名查询数据库，如果查询结果数量等于0，说明没有注册
                    如果查询结果数量等于1，说明有注册
    响应          JSON 
                {code:0,count:0/1,errmsg:ok}

    路由      GET              
   步骤：
        1.  接收用户名
        2.  根据用户名查询数据库
        3.  返回响应         
this.host + '/mobiles/' + this.mobile + '/count/';
axios.get
"""


class MobileCountView(View):
    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'count': count})


##############################################################################################
"""
我们不相信前端提交的任何数据！！！！
前端：     当用户输入 用户名，密码，确认密码，手机号，是否同意协议之后，会点击注册按钮
            前端会发送axios请求
后端：
    请求：             接收请求（JSON）。获取数据
    业务逻辑：          验证数据。数据入库
    响应：             JSON {'code':0,'errmsg':'ok'}
                     响应码 0 表示成功 400表示失败
    路由：     POST    register/
    步骤：
        1. 接收请求（POST------JSON） this.host + '/register/'
        发送的json数据：
        {
                    username: this.username,
                    password: this.password,
                    password2: this.password2,
                    mobile: this.mobile,
                    sms_code: this.sms_code,
                    allow: this.allow
                }
        2. 获取数据
        3. 验证数据
            3.1 用户名，密码，确认密码，手机号，是否同意协议 都要有
            3.2 用户名满足规则，用户名不能重复
            3.3 密码满足规则
            3.4 确认密码和密码要一致
            3.5 手机号满足规则，手机号也不能重复
            3.6 需要同意协议
        4. 数据入库
        5. 返回响应
"""
import json
import re


class RegisterView(View):
    def post(self, request):
        # 1.接收请求 post--json
        body_bytes = request.body
        body_str = body_bytes.decode()
        body_dict = json.loads(body_str)

        # 2.获取数据
        username = body_dict.get('username')
        password = body_dict.get('password')
        password2 = body_dict.get('password2')
        mobile = body_dict.get('mobile')
        allow = body_dict.get('allow')
        sms_code = body_dict.get('sms_code')  # 短信验证码

        # 3.验证数据
        # 3.1 用户名 密码 确认密码 手机号 是否同意协议都要有
        # all([xxx,xxx,xxx]) all里面的元素只要有none，就会返回false,否则返回true
        if not all([username, password, password2, mobile, allow, sms_code]):
            return JsonResponse({'code': 400, 'message': '参数不全'})
        # 3.2 用户名满足规则 用户名不能重复（已经判断）
        if not re.match('[a-zA-Z_-]{5,20}', username):
            return JsonResponse({'code': 400, 'errmsg': '用户名不满足规则'})
        # 3.3 密码满足规则
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return JsonResponse({'code': 400, 'errmsg': 'password格式有误!'})
        # 3.4 确认密码和密码一致
        if password2 != password:
            return JsonResponse({'code': 400, 'errmsg': '两次输入密码不对'})
        # 3.5 手机号满足规则 ，手机号不能重复（已判断）
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400, 'errmsg': '手机号格式有误!'})
        # 3.6.手机验证码
        # 取出存在redis里的手机验证码
        from django_redis import get_redis_connection
        redis_conn = get_redis_connection('code')
        sms_code_server = redis_conn.get(mobile)
        # 判断验证码是否过期
        if not sms_code_server:
            return JsonResponse({'code': 400, 'errmsg': '短信验证码失效'})
        # 对比用户输入的和服务器存储的短信验证码是否一致
        if sms_code != sms_code_server.decode():
            return JsonResponse({'code': 400, 'errmsg': '短信验证码有误'})

        # 3.7  判断是否勾选用户协议
        if allow != True:
            return JsonResponse({'code': 400, 'errmsg': 'allow格式有误!'})
        # 4.数据库入库
        # 第一种
        # user = User(username=username,password=password,mobile=mobile)
        # user.save()
        # 第二种
        # User.objects.create(username=username,password=password,mobile=mobile)
        # 以上2中方式，都是可以数据入库的
        # 但是 有一个问题 密码没有加密

        # 密码加密
        user = User.objects.create_user(username=username, password=password, mobile=mobile)

        # 如何设置session信息
        # request.session['user_id']=user.id

        # 系统（Django）为我们提供了 状态保持的方法
        from django.contrib.auth import login
        # request, user,
        # 状态保持 -- 登录用户的状态保持
        # user 已经登录的用户信息
        login(request, user)

        # 5.返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


"""
如果需求是注册成功后即表示用户认证通过，那么此时可以在注册成功后实现状态保持 (注册成功即已经登录)  v
如果需求是注册成功后不表示用户认证通过，那么此时不用在注册成功后实现状态保持 (注册成功，单独登录)

实现状态保持主要有两种方式：
    在客户端存储信息使用Cookie
    在服务器端存储信息使用Session
    
状态保持：
将通过认证的用户的唯一标识信息（比如：用户ID）写入到当前session会话中
login()方法：
Django用户认证系统提供了 login() 方法
封装了写入session的操作，帮助我们快速实现状态保持
login()位置：

django.contrib.auth.__init__.py文件中
login(request, user)

"""

"""
登录

前端：
        当用户把用户名和密码输入完成之后，会点击登录按钮。这个时候前端应该发送一个axios请求

后端：
    请求    ：  接收数据，验证数据
    业务逻辑：   验证用户名和密码是否正确，session
    响应    ： 返回JSON数据 0 成功。 400 失败

    POST        /login/
    axios.post(this.host + '/login/', {
                    username: this.username,
                    password: this.password,
                    remembered:this.remember,
                }
步骤：
    1. 接收数据
    2. 验证数据
    3. 验证用户名和密码是否正确
    4. session
    5. 判断是否记住登录
    6. 返回响应

Ctrl + Alt + L
"""


class LoginView(View):
    def post(self, request):
        # 1.接收数据
        data = json.loads(request.body.decode())
        username = data.get('username')
        password = data.get('password')
        remembered = data.get('remembered')

        #  2.验证数据
        if not all([username, password]):
            return JsonResponse({'code': 400, 'errmsg': '参数不全'})

        # 确定我们是通过手机号查询 还是用户名查询
        # USERNAME_FIELD 我们可以根据 修改 User. USERNAME_FIELD 字段来影响authenticate 的查询
        # authenticate 就是根据 USERNAME_FIELD 来查询
        if re.match('1[3-9]\d{9}', username):
            User.USERNAME_FIELD = 'mobile'
        else:
            User.USERNAME_FIELD = 'username'

        # 3.验证用户名和密码是否正确
        # 方式一 我们可以通过模型根据用户名来查询
        # User.objects.get(username=username)

        # 方式二
        from django.contrib.auth import authenticate
        user = authenticate(username=username, password=password)

        if user is None:
            return JsonResponse({'code': 400, 'errmsg': '账号或密码错误'})

        # 4.session
        from django.contrib.auth import login
        login(request, user)

        # 5.判断是否记住
        if remembered:
            # 记住登录 -- 2周 或者 1个月 具体多长时间 产品说了算
            request.session.set_expiry(None)
        else:
            request.session.set_expiry(0)  # 不记住登录  浏览器关闭 session过期
        # 6 返回响应
        reponse = JsonResponse({'code': 0, 'errmsg': 'ok'})
        reponse.set_cookie('username', username)  # 用户名写入cookie

        return reponse


"""
前端：
    当用户点击退出按钮的时候，前端发送一个axios delete请求
     var url = this.host + '/logout/';

后端：
    请求
    业务逻辑        退出
    响应      返回JSON数据

"""
from django.contrib.auth import logout


class LogoutView(View):
    def delete(self, request):
        # 1.删除session信息
        logout(request)
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        # 2.删除cookie信息，为什么要是删除呢？ 因为前端是根据cookie信息来判断用户是否登录的
        response.delete_cookie('username')
        return response


# 用户中心，也必须是登录用户
"""
LoginRequiredMixin 未登录的用户 会返回重定向。重定向并不是JSON数据
我们需要是  返回JSON数据

Django用户认证系统提供了方法
request.user.is_authenticated来判断用户是否登录。如果通过登录验证则返回True。反之，返回False。
LoginRequiredMixin封装了判断用户是否登录的操作。
"""
from utils.views import LoginRequiredJSONMixin


class CenterView(LoginRequiredJSONMixin, View):
    def get(self, request):
        # request.user 就是 已经登录的用户信息
        # request.user 是来源于 中间件
        # 系统会进行判断 如果我们确实是登录用户，则可以获取到 登录用户对应的 模型实例数据
        # 如果我们确实不是登录用户，则request.user = AnonymousUser()  匿名用户
        info_data = {
            'username': request.user.username,
            'email': request.user.email,
            'mobile': request.user.mobile,
            'email_active': request.user.email_active,
        }

        return JsonResponse({'code': 0, 'errmsg': 'ok', 'info_data': info_data})


"""
需求：     1.保存邮箱地址  2.发送一封激活邮件  3. 用户激活邮件

前端：
    当用户输入邮箱之后，点击保存。这个时候会发送axios请求。

后端：
    请求           接收请求，获取数据
    业务逻辑        保存邮箱地址  发送一封激活邮件
    响应           JSON  code=0

    路由          PUT     
    步骤
        1. 接收请求
        2. 获取数据
        3. 保存邮箱地址
        4. 发送一封激活邮件
        5. 返回响应


需求（要实现什么功能） --> 思路（ 请求。业务逻辑。响应） --> 步骤  --> 代码实现
"""


class EmailView(LoginRequiredJSONMixin, View):
    def put(self, request):
        # 1. 接收请求
        # put post －－－　ｂｏdy
        data = json.loads(request.body.decode())
        # 2.获取数据
        email = data.get('email')
        # 验证数据
        if not email:
            return JsonResponse({'code': 400, 'errmsg': '缺少email参数'})
        # 正则
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
             return JsonResponse({'code': 400, 'errmsg': '参数email有误'})
        #3.赋值email字段
        try:
            request.user.email=email
            request.user.save()
        except Exception as e:
            return JsonResponse({'code':400,'errmsg':'添加邮箱失败'})

        from django.core.mail import send_mail
        #subject 主题
        subject = '美多商城激活邮件'
        # message,      邮件内容
        message = ''
        # from_email,   发件人
        from_email = '美多商城<wwklhx@163.com>'
        # recipient_list, 收件人列表
        recipient_list = [email]

        #对user.id加密
        from apps.users.utils import generic_email_verify_token
        token =generic_email_verify_token(request.user.id)

        # 4.1邮件内容
        verify_url = "http://www.meiduo.site:8080/success_verify_email.html?token=%s" % token
        # 4.2组织我们的激活邮件 带网页标签的
        html_message = '<p>尊敬的用户您好！</p>' \
                       '<p>感谢您使用美多商城。</p>' \
                       '<p>您的邮箱为：%s 。请点击此链接激活您的邮箱：</p>' \
                       '<p><a href="%s">%s<a></p>' % (email, verify_url, verify_url)
        send_mail(subject=subject,
                  message=message,
                  from_email=from_email,
                  recipient_list=recipient_list,
                  html_message=html_message)

        ##使用异步发送邮件
        # from celery_tasks.email.tasks import celery_send_email
        # celery_send_email.delay(
        #     subject=subject,
        #     message=message,
        #     from_email=from_email,
        #     recipient_list=recipient_list,
        #     html_message=html_message
        # )

        #响应添加邮箱结果
        return JsonResponse({'code':0,'errmsg':'添加邮箱成功'})

"""

1. 设置邮件服务器

    我们设置 163邮箱服务器
    相当于 我们开启了 让163帮助我们发送邮件。同时设置了 一些信息（特别是授权码）

2.  设置邮件发送的配置信息
    #  让django的哪个类来发送邮件
    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    # 邮件服务器的主机和端口号
    EMAIL_HOST = 'smtp.163.com'
    EMAIL_PORT = 25

    # 使用我的 163服务器 和 授权码
    #发送邮件的邮箱
    EMAIL_HOST_USER = 'wwklhx@163.com'
    #在邮箱中设置的客户端授权密码
    EMAIL_HOST_PASSWORD = '123456abc'

3. 调用  send_mail 方法
"""

"""
需求（知道我们要干什么？？？）：
    激活用户的邮件
前端(用户干了什么，传递了什么参数)：
        用户会点击那个激活连接。那个激活连接携带了token
后端：
    请求：         接收请求，获取参数，验证参数
    业务逻辑：       user_id, 根据用户id查询数据，修改数据
    响应：         返回响应JSON

    路由：         PUT     emails/verification/  说明： token并没有在body里
    步骤：

        1. 接收请求
        2. 获取参数
        3. 验证参数
        4. 获取user_id
        5. 根据用户id查询数据
        6. 修改数据
        7. 返回响应JSON

"""
class EmailVerifyView(View):
    def put(self,request):
        #1.接收参数
        params = request.GET
        #2.获取参数
        token = params.get('token')
        #3.验证参数
        if token is None:
            return JsonResponse({'code':400,'errmsg':'参数缺失'})
        #4.获取user_id
        from apps.users.utils import check_verify_token
        user_id=check_verify_token(token)

        if user_id is None:
            return JsonResponse({'code':400,'errmsg':'参数错误'})

        #5.根据用户id查询数据
        user = User.objects.get(id=user_id)

        #6.修改数据
        user.email_active=True
        user.save()
        #7.返回响应的JSON
        return JsonResponse({'code':0,'errmsg':'ok'})


"""
需求：
    新增地址

前端：
        当用户填写完成地址信息后，前端应该发送一个axios请求，会携带 相关信息 （POST--body）

后端：

    请求：         接收请求，获取参数,验证参数
    业务逻辑：      数据入库
    响应：         返回响应

    路由：     POST        /addresses/create/
    步骤： 
        1.接收请求
        2.获取参数，验证参数
        3.数据入库
        4.返回响应

"""
#保存地址数据
from apps.users.models import Address
class AddressCreateView(LoginRequiredJSONMixin,View):
    def post(self,request):
        # 0判断是否超过地址上限：最多20个
        # Address.objects.filter(user=request.user).count()
        count = request.user.addresses.count()
        if count >=20:
            return JsonResponse({'code':400,'errmsg':'超过地址数量上限'})

        #1.接收请求
        data = json.loads(request.body.decode())
        #2.获取参数 验证参数
        receiver = data.get('receiver')
        province_id = data.get('province_id')
        city_id = data.get('city_id')
        district_id = data.get('district_id')
        place = data.get('place')
        mobile = data.get('mobile')
        tel = data.get('tel')
        email = data.get('email')

        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return JsonResponse({'code': 400, 'errmsg': '缺少必传参数'})
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400, 'errmsg': '参数mobile有误'})

        if not re.match(r'^1[3-9]\d{9}$',tel):
                return JsonResponse({'code': 400, 'errmsg': '参数tel有误'})

        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return JsonResponse({'code': 400, 'errmsg': '参数email有误'})

        user = request.user

        #3.验证参数
        #4.数据入库
        address = Address.objects.create(
            user=user,
            title=receiver,
            receiver=receiver,
            province_id=province_id,
            city_id=city_id,
            district_id=district_id,
            place=place,
            mobile=mobile,
            tel=tel,
            email=email
        )

        if not user.default_address:
            user.default_address = address
            user.save()

        # 新增地址成功，将新增的地址响应给前端实现局部刷新
        address_dict = {
            'id': address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        #返回响应
        return JsonResponse({'code':0,'errmsg':'ok','address':address_dict})

#查询地址数据
class AddressView(LoginRequiredJSONMixin,View):
    def get(self,request):
        # 1.查询指定数据
        user=request.user
        # addresses=user.addresses

        addresses=Address.objects.filter(user=user,is_deleted=False)
        # 2.将对象数据转换为字典数据
        address_list=[]
        for address in addresses:
            address_dict={
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
            #将默认地址移动到最前面
            default_address = request.user.default_address
            if default_address == address:
                address_list.insert(0,address_dict)
            else:
               address_list.append(address_dict)
        # 3.返回响应
        return JsonResponse({'code':0,'errmsg':'ok','addresses':address_list})


#更新或删除地址数据
class UpdateDestroyAddressView(LoginRequiredJSONMixin, View):
    def put(self,request,address_id):
        #修改地址
        #接收参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 判断地址是否存在,并更新地址信息
        try:
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            return JsonResponse({'code':400,'errmsg':'更新地址失败'})
        #构造响应数据
        address = Address.objects.get(id=address_id)
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        return JsonResponse({'code': 0, 'errmsg': '更新地址成功', 'address': address_dict})

    def delete(self, request, address_id):
        """删除地址"""
        try:
            # 查询要删除的地址
            address = Address.objects.get(id=address_id)
            # 将地址逻辑删除设置为True
            address.is_deleted = True
            address.save()
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': '删除地址失败'})

        # 响应删除地址结果
        return JsonResponse({'code': 0, 'errmsg': '删除地址成功'})

#设置默认地址
class DefaultAddressView(LoginRequiredJSONMixin, View):
    def put(self,request,address_id):
        try:
            address = Address.objects.get(id=address_id)
            request.user.default_address=address

            request.user.save()
        except Exception as e:
            return  JsonResponse({'code':400,'errmsg':'设置默认地址失败'})
        return JsonResponse({'code': 0, 'errmsg': '设置默认地址成功'})

#修改地址标题
class UpdateTitleAddressView(LoginRequiredJSONMixin, View):
    """设置地址标题"""

    def put(self, request, address_id):
        """设置地址标题"""
        # 接收参数：地址标题
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')

        try:
            # 查询地址
            address = Address.objects.get(id=address_id)

            # 设置新的地址标题
            address.title = title
            address.save()
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': '设置地址标题失败'})

        # 4.响应删除地址结果
        return JsonResponse({'code': 0, 'errmsg': '设置地址标题成功'})


class ChangePasswordView(LoginRequiredJSONMixin, View):
    """修改密码"""
    def put(self, request):
        """实现修改密码逻辑"""
        # 接收参数
        dict = json.loads(request.body.decode())
        old_password = dict.get('old_password')
        new_password = dict.get('new_password')
        new_password2 = dict.get('new_password2')

        # 校验参数
        if not all([old_password, new_password, new_password2]):
           return JsonResponse({'code':400,
                                     'errmsg':'缺少必传参数'})


        result = request.user.check_password(old_password)
        if not result:
            return JsonResponse({'code':400,
                                      'errmsg':'原始密码不正确'})

        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_password):
            return JsonResponse({'code':400,
                                      'errmsg':'密码最少8位,最长20位'})

        if new_password != new_password2:
            return JsonResponse({'code':400,
                                      'errmsg':'两次输入密码不一致'})

        # 修改密码
        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:

            return JsonResponse({'code':400,
                                      'errmsg':'修改密码失败'})

        # 清理状态保持信息
        logout(request)

        response = JsonResponse({'code':0,
                                      'errmsg':'ok'})

        response.delete_cookie('username')

        # # 响应密码修改结果：重定向到登录界面
        return response


# class TestPasswd(LoginRequiredJSONMixin,View):
#     def put(self,request):
#         #接收参数‘
#         dict=json.loads(request.body.decode())
#         old_password = dict.get('old_password')
#         new_password = dict.get('new_password')
#         new_password2=dict.get('new_password2')
#
#         #校验参数
#         if not all([old_password,new_password,new_password2]):
#             return JsonResponse({'code':400,'errmsg':'缺少必要参数'})
#
#
#         result = request.user.check_password(old_password)
#         if not result:
#             return JsonResponse({'code':400,'errmsg':'原始密码不正确'})
#
#         #修改密码
#         try:
#             request.user.set_password(new_password)
#             request.user.save()
#         except Exception as e:
#             return JsonResponse({'code':400,'errmsg':'修改密码失败'})
#         #清理状态保持信息
#
#         logout(request)
#         response = JsonResponse({'code':0,'errmsg':'ok'})
#         response.delete_cookie('username')
#         return response


        pass









